﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Gunningine
{
    public class Animation
    {
        private Action<Entity, WorldState>[] frameEvents = null;

        /// <summary>
        /// Animation name
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// Current frame
        /// </summary>
        public int CurFrame { get; private set; }

        /// <summary>
        /// Total number of frames
        /// </summary>
        public int NumFrames { get; private set; }

        /// <summary>
        /// Current time
        /// </summary>
        public float Time { get; private set; }

        /// <summary>
        /// Time scale factor.
        /// </summary>
        public float TimeScale { get; private set; }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="name"></param>
        internal Animation(string name, int frames, float timeScale)
        {
            this.Name = name;
            this.NumFrames = frames;
            this.TimeScale = timeScale;

            this.CurFrame = 0;
            this.Time = 0;

            this.frameEvents = new Action<Entity, WorldState>[frames];
            for (int i = 0; i < frames; ++i)
                this.frameEvents[i] = null;
        }

        /// <summary>
        /// Set the current animation.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="frames"></param>
        internal void Reset()
        {
            this.CurFrame = 0;
            this.Time = 0;
        }

        /// <summary>
        /// Set time scale.
        /// </summary>
        /// <param name="timeScale"></param>
        internal void SetTimeScale(float timeScale)
        {
            this.TimeScale = TimeScale;
        }

        /// <summary>
        /// Set the event for a frame.
        /// </summary>
        /// <param name="frame"></param>
        /// <param name="handler"></param>
        internal void SetFrameEvent(int frame, Action<Entity, WorldState> handler)
        {
            if (frame < 0 || frame >= this.NumFrames)
                throw new GameException(String.Format("Could not set event for frame {0} of {1} because it is out of bounds.", frame, this.Name));

            this.frameEvents[frame] = handler;
        }

        /// <summary>
        /// Update animation.
        /// </summary>
        /// <param name="frameTime"></param>
        internal void Update(float frameTime, Entity owner, WorldState worldState)
        {
            this.Time += frameTime;

            float animTimeFreq = 1.0f / this.TimeScale;
            if (this.Time > animTimeFreq)
            {
                this.Time -= animTimeFreq;
                this.CurFrame = (this.CurFrame + 1) % this.NumFrames;
                
                // Does this frame have an event attached to it?
                if (this.frameEvents[this.CurFrame] != null)
                    this.frameEvents[this.CurFrame](owner, worldState);
            }
        }
    }
}
